/* ----------------------------------------------------------------- */
/*           The HMM-Based Speech Synthesis Engine "hts_engine API"  */
/*           developed by HTS Working Group                          */
/*           http://hts-engine.sourceforge.net/                      */
/* ----------------------------------------------------------------- */
/*                                                                   */
/*  Copyright (c) 2001-2010  Nagoya Institute of Technology          */
/*                           Department of Computer Science          */
/*                                                                   */
/*                2001-2008  Tokyo Institute of Technology           */
/*                           Interdisciplinary Graduate School of    */
/*                           Science and Engineering                 */
/*                                                                   */
/* All rights reserved.                                              */
/*                                                                   */
/* Redistribution and use in source and binary forms, with or        */
/* without modification, are permitted provided that the following   */
/* conditions are met:                                               */
/*                                                                   */
/* - Redistributions of source code must retain the above copyright  */
/*   notice, this list of conditions and the following disclaimer.   */
/* - Redistributions in binary form must reproduce the above         */
/*   copyright notice, this list of conditions and the following     */
/*   disclaimer in the documentation and/or other materials provided */
/*   with the distribution.                                          */
/* - Neither the name of the HTS working group nor the names of its  */
/*   contributors may be used to endorse or promote products derived */
/*   from this software without specific prior written permission.   */
/*                                                                   */
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND            */
/* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,       */
/* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF          */
/* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE          */
/* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS */
/* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,          */
/* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED   */
/* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,     */
/* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */
/* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,   */
/* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY    */
/* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE           */
/* POSSIBILITY OF SUCH DAMAGE.                                       */
/* ----------------------------------------------------------------- */
/**
 * Copyright 2011 DFKI GmbH.
 * All Rights Reserved.  Use is subject to license terms.
 *
 * This file is part of MARY TTS.
 *
 * MARY TTS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package marytts.htsengine;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

import marytts.cart.CART;
import marytts.cart.DecisionNode;
import marytts.cart.DecisionNode.BinaryByteDecisionNode;
import marytts.features.FeatureDefinition;
import marytts.util.MaryUtils;

import org.apache.logging.log4j.Logger;

/**
 * Set of Global Mean and (diagonal) Variance for log f0, mel-cepstrum, bandpass voicing strengths and Fourier magnitudes (
 * 
 * Java port and extension of HTS engine API version 1.04 Extension: mixed excitation
 * 
 * @author Marcela Charfuelan
 */
public class GVModelSet {

	/**
	 * ____________________ GV related variables ____________________ GV: Global mean and covariance (diagonal covariance only) it
	 * should be inverse for GV method Gradient weight by default is 1.0 but it can be between 0.0-2.0
	 */
	private double gvmeanMgc[];
	private double gvcovInvMgc[];

	private double gvmeanLf0[];
	private double gvcovInvLf0[];

	private double gvmeanStr[];
	private double gvcovInvStr[];

	private double gvmeanMag[];
	private double gvcovInvMag[];

	private Logger logger = MaryUtils.getLogger("GVModelSet");

	public double[] getGVmeanMgc() {
		return gvmeanMgc;
	}

	public double[] getGVcovInvMgc() {
		return gvcovInvMgc;
	}

	public double[] getGVmeanLf0() {
		return gvmeanLf0;
	}

	public double[] getGVcovInvLf0() {
		return gvcovInvLf0;
	}

	public double[] getGVmeanStr() {
		return gvmeanStr;
	}

	public double[] getGVcovInvStr() {
		return gvcovInvStr;
	}

	public double[] getGVmeanMag() {
		return gvmeanMag;
	}

	public double[] getGVcovInvMag() {
		return gvcovInvMag;
	}

	public void loadGVModelSet(HMMData htsData, FeatureDefinition featureDef) throws IOException {

		/* allocate memory for the arrays and load the data from file */
		int numMSDFlag, numStream, vectorSize, numDurPdf;
		double gvcov;
		DataInputStream data_in;
		InputStream gvStream;

		/* Here global variance vectors are loaded from corresponding files */
		int m, i, nmix;
		if (htsData.getUseGV()) {
			// GV for Mgc
			if ((gvStream = htsData.getPdfMgcGVStream()) != null)
				loadGvFromFile(gvStream, "mgc", htsData.getGvMethodGradient(), htsData.getGvWeightMgc());

			// GV for Lf0
			if ((gvStream = htsData.getPdfLf0GVStream()) != null)
				loadGvFromFile(gvStream, "lf0", htsData.getGvMethodGradient(), htsData.getGvWeightLf0());

			// GV for Str
			if ((gvStream = htsData.getPdfStrGVStream()) != null)
				loadGvFromFile(gvStream, "str", htsData.getGvMethodGradient(), htsData.getGvWeightStr());

			// GV for Mag
			if ((gvStream = htsData.getPdfMagGVStream()) != null)
				loadGvFromFile(gvStream, "mag", htsData.getGvMethodGradient(), htsData.getGvWeightMag());

			// gv-switch
			// if( (gvFile=htsData.getSwitchGVFile()) != null)
			// loadSwitchGvFromFile(gvFile, featureDef, trickyPhones);

		}

	}

	private void loadGvFromFile(InputStream gvStream, String par, boolean gradientMethod, double gvWeight) throws IOException {

		int numMSDFlag, numStream, vectorSize, numDurPdf;
		DataInputStream data_in;
		int m, i;

		data_in = new DataInputStream(new BufferedInputStream(gvStream));
		logger.debug("LoadGVModelSet reading model of type '" + par + "' with gvWeight = " + gvWeight);

		numMSDFlag = data_in.readInt();
		numStream = data_in.readInt();
		vectorSize = data_in.readInt();
		numDurPdf = data_in.readInt();

		if (par.contentEquals("mgc")) {
			gvmeanMgc = new double[vectorSize];
			gvcovInvMgc = new double[vectorSize];
			readBinaryFile(data_in, gvmeanMgc, gvcovInvMgc, vectorSize, gradientMethod, gvWeight);
		} else if (par.contentEquals("lf0")) {
			gvmeanLf0 = new double[vectorSize];
			gvcovInvLf0 = new double[vectorSize];
			readBinaryFile(data_in, gvmeanLf0, gvcovInvLf0, vectorSize, gradientMethod, gvWeight);
		} else if (par.contentEquals("str")) {
			gvmeanStr = new double[vectorSize];
			gvcovInvStr = new double[vectorSize];
			readBinaryFile(data_in, gvmeanStr, gvcovInvStr, vectorSize, gradientMethod, gvWeight);
		} else if (par.contentEquals("mag")) {
			gvmeanMag = new double[vectorSize];
			gvcovInvMag = new double[vectorSize];
			readBinaryFile(data_in, gvmeanMag, gvcovInvMag, vectorSize, gradientMethod, gvWeight);
		}
		data_in.close();
	}

	private void readBinaryFile(DataInputStream data_in, double mean[], double ivar[], int vectorSize, boolean gradientMethod,
			double gvWeight) throws IOException {
		int i;
		double var;
		if (gradientMethod) {
			for (i = 0; i < vectorSize; i++) {
				mean[i] = data_in.readFloat() * gvWeight;
				var = data_in.readFloat();
				assert var > 0.0;
				ivar[i] = 1.0 / var;
			}
		} else {
			for (i = 0; i < vectorSize; i++) {
				mean[i] = data_in.readFloat() * gvWeight;
				ivar[i] = data_in.readFloat();
			}
		}
	}

	public void loadSwitchGvFromFile(String gvFile, FeatureDefinition featDef, PhoneTranslator trickyPhones) throws Exception {

		// featDef = featDefinition;
		// phTrans = phoneTranslator;
		PhoneTranslator phTrans = trickyPhones;

		int i, j, length, state, feaIndex;
		BufferedReader s = null;
		String line, buf, aux;
		StringTokenizer sline;
		// phTrans = phTranslator;

		assert featDef != null : "Feature Definition was not set";

		try {
			/* read lines of tree-*.inf fileName */
			s = new BufferedReader(new InputStreamReader(new FileInputStream(gvFile)));
			logger.info("load: reading " + gvFile);

			// skip questions section
			while ((line = s.readLine()) != null) {
				if (line.indexOf("QS") < 0)
					break; /* a new state is indicated by {*}[2], {*}[3], ... */
			}

			while ((line = s.readLine()) != null) {
				if (line.indexOf("{*}") >= 0) { /* this is the indicator of a new state-tree */
					aux = line.substring(line.indexOf("[") + 1, line.indexOf("]"));
					state = Integer.parseInt(aux);

					sline = new StringTokenizer(aux);

					/* 1: gets index node and looks for the node whose idx = buf */
					buf = sline.nextToken();

					/* 2: gets question name and question name val */
					buf = sline.nextToken();
					String[] fea_val = buf.split("="); /* splits featureName=featureValue */
					feaIndex = featDef.getFeatureIndex(fea_val[0]);

					/* Replace back punctuation values */
					/* what about tricky phones, if using halfphones it would not be necessary */
					if (fea_val[0].contentEquals("sentence_punc") || fea_val[0].contentEquals("prev_punctuation")
							|| fea_val[0].contentEquals("next_punctuation")) {
						// System.out.print("CART replace punc: " + fea_val[0] + " = " + fea_val[1]);
						fea_val[1] = phTrans.replaceBackPunc(fea_val[1]);
						// System.out.println(" --> " + fea_val[0] + " = " + fea_val[1]);
					} else if (fea_val[0].contains("tobi_")) {
						// System.out.print("CART replace tobi: " + fea_val[0] + " = " + fea_val[1]);
						fea_val[1] = phTrans.replaceBackToBI(fea_val[1]);
						// System.out.println(" --> " + fea_val[0] + " = " + fea_val[1]);
					} else if (fea_val[0].contains("phone")) {
						// System.out.print("CART replace phone: " + fea_val[0] + " = " + fea_val[1]);
						fea_val[1] = phTrans.replaceBackTrickyPhones(fea_val[1]);
						// System.out.println(" --> " + fea_val[0] + " = " + fea_val[1]);
					}

					// add featureName and featureValue to the switch off gv phones

				}
			} /* while */
			if (s != null)
				s.close();

		} catch (FileNotFoundException e) {
			logger.debug("FileNotFoundException: " + e.getMessage());
			throw new FileNotFoundException("LoadTreeSet: " + e.getMessage());
		}

	}
}
